﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Threading.Tasks;
using landlord2lib;

namespace landlord2
{
    /// <summary>
    /// CardGroup.xaml 的交互逻辑
    /// </summary>
    public partial class CardGroup : UserControl, IComparer<Card>
    {
        private static readonly Size CardSize = new Size(73, 98);
        private static readonly Size SmallCardSize = new Size(36, 49);
        private static readonly int CardMargin = 20;
        private static readonly int SmallCardMargin = 10;
        private static readonly int LineMargin = 30;
        private static readonly int SmallLineMargin = 15;
        private static readonly int SelectMargin = 10;
        private static readonly int ShaddowSize = 6;
        private static readonly int WrapCardCount = 13;

        private List<Card> m_cards = new List<Card>();
        private List<bool> m_selectedList = new List<bool>();
        private bool m_selectedMode;

        public CardGroup()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(CardGroupMode), typeof(CardGroup),
            new FrameworkPropertyMetadata(CardGroupMode.Normal));

        public CardGroupMode Mode
        {
            get
            {
                return (CardGroupMode)GetValue(ModeProperty);
            }
            set
            {
                SetValue(ModeProperty, value);
            }
        }

        public static readonly DependencyProperty CardStyleProperty = DependencyProperty.Register("CardStyle", typeof(int), typeof(CardGroup),
            new FrameworkPropertyMetadata(0, OnCardStylePropertyChanged));

        public int CardStyle
        {
            get
            {
                return (int)GetValue(CardStyleProperty);
            }
            set
            {
                SetValue(CardStyleProperty, value);
            }
        }

        private static void OnCardStylePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            CardGroup cardGroup = (CardGroup)obj;
            cardGroup.ShowCard();
        }

        public static readonly DependencyProperty SelectableProperty = DependencyProperty.Register("Selectable", typeof(bool), typeof(CardGroup),
            new FrameworkPropertyMetadata(false));

        public bool Selectable
        {
            get
            {
                return (bool)GetValue(SelectableProperty);
            }
            set
            {
                SetValue(SelectableProperty, value);
            }
        }

        public static readonly RoutedEvent SelectChangedEvent = EventManager.RegisterRoutedEvent("SelectChanged", RoutingStrategy.Bubble,
            typeof(RoutedEventHandler), typeof(CardGroup));

        public event RoutedEventHandler SelectChanged
        {
            add { AddHandler(SelectChangedEvent, value); }
            remove { RemoveHandler(SelectChangedEvent, value); }
        }

        private void RaiseSelectChangedEvent()
        {
            RaiseEvent(new RoutedEventArgs(SelectChangedEvent, this));
        }

        public void AddCard(Card card)
        {
            m_cards.Add(card);
            m_selectedList.Add(false);
            Image img = NewCardImage(card);
            gridContent.Children.Add(img);
            ReLayout();
        }

        public void AddCards(ICollection<Card> cards)
        {
            m_cards.AddRange(cards);
            foreach (Card card in cards)
            {
                m_selectedList.Add(false);
                Image img = NewCardImage(card);
                gridContent.Children.Add(img);
            }
            ReLayout();
        }

        public void RemoveCard(Card card)
        {
            RemoveCard(card, true);
        }
        private void RemoveCard(Card card, bool layout)
        {
            int index = m_cards.IndexOf(card);
            if (index < 0) return;
            RemoveCardAt(index, layout);
        }

        public void RemoveCardAt(int index)
        {
            RemoveCardAt(index, true);
        }

        private void RemoveCardAt(int index, bool layout)
        {
            gridContent.Children.RemoveAt(index);
            m_cards.RemoveAt(index);
            m_selectedList.RemoveAt(index);
            if (layout) ReLayout();
        }

        public void RemoveCards(ICollection<Card> cards)
        {
            ClearSelect(false);
            foreach (Card card in cards)
            {
                int index = m_cards.IndexOf(card);
                m_cards.Remove(card);
                m_selectedList.RemoveAt(index);
                gridContent.Children.RemoveAt(gridContent.Children.Count - 1);
            }
            ShowCard();
            ReLayout();
        }

        public void ClearCards()
        {
            m_cards.Clear();
            m_selectedList.Clear();
            gridContent.Children.Clear();
            ReLayout();
        }

        public int GetCardCount()
        {
            return m_cards.Count;
        }

        public Card[] GetCards()
        {
            return m_cards.ToArray();
        }

        public void ShowBack()
        {
            foreach (UIElement el in gridContent.Children)
            {
                Image img = (Image)el;
                img.Source = (BitmapSource)App.Current.Resources[string.Format("{0}_Back", GetCardStyleString(CardStyle))];
            }
        }

        public void ShowCard()
        {
            int i = 0;
            foreach (UIElement el in gridContent.Children)
            {
                Image img = (Image)el;
                img.Source = (BitmapSource)App.Current.Resources[GetCardResourceString(CardStyle, m_cards[i])];
                i++;
            }
        }

        private Image NewCardImage(Card card)
        {
            Image img = new Image();
            img.Style = Mode == CardGroupMode.Normal ? (Style)App.Current.Resources["CardStyle"] : (Style)App.Current.Resources["SmallCardStyle"];
            img.HorizontalAlignment = HorizontalAlignment.Left;
            img.VerticalAlignment = VerticalAlignment.Top;
            img.Source = (BitmapSource)App.Current.Resources[GetCardResourceString(CardStyle, card)];
            if (Selectable)
            {
                img.MouseLeftButtonDown += CardImage_MouseLeftButtonDown;
                img.MouseRightButtonDown += CardImage_MouseRightButtonDown;
                img.MouseMove += CardImage_MouseMove;
                img.MouseLeftButtonUp += CardImage_MouseLeftButtonUp;
            }
            return img;
        }

        private void CardImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            RaiseSelectChangedEvent();
        }

        private void CardImage_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Image img = (Image)sender;
                int index = gridContent.Children.IndexOf(img);
                if (index < 0) return;
                if (!m_selectedMode)
                {
                    if (m_selectedList[index])
                    {
                        m_selectedList[index] = false;
                        SetImageMargin(img, false);
                    }
                }
                else
                {
                    if (!m_selectedList[index])
                    {
                        m_selectedList[index] = true;
                        SetImageMargin(img, true);
                    }
                }
            }
        }

        private void CardImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Image img = (Image)sender;
            int index = gridContent.Children.IndexOf(img);
            if (index < 0) return;
            if (m_selectedList[index])
            {
                m_selectedMode = false;
                m_selectedList[index] = false;
                SetImageMargin(img, false);
            }
            else
            {
                m_selectedMode = true;
                m_selectedList[index] = true;
                SetImageMargin(img, true);
            }
        }

        public static readonly RoutedEvent ItemRightClickEvent = EventManager.RegisterRoutedEvent("ItemRightClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CardGroup));

        public event RoutedEventHandler ItemRightClick
        {
            add { AddHandler(ItemRightClickEvent, value); }
            remove { RemoveHandler(ItemRightClickEvent, value); }
        }

        private void RaiseItemRightClickEvent()
        {
            RoutedEventArgs args = new RoutedEventArgs(ItemRightClickEvent);
            RaiseEvent(args);
        }

        private void CardImage_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            RaiseItemRightClickEvent();
        }

        public void ClearSelect()
        {
            ClearSelect(true);
        }

        private void ClearSelect(bool anim)
        {
            bool hasChanged = false;
            for (int i = 0; i < m_selectedList.Count; i++)
            {
                if (m_selectedList[i])
                {
                    hasChanged = true;
                    m_selectedList[i] = false;
                    SetImageMargin((Image)gridContent.Children[i], false);
                }
            }
            if (hasChanged) RaiseSelectChangedEvent();
        }

        private delegate void ShowBackDelegate();
        private delegate void ShowCardDelegate();
        public void SortCard(int mode)
        {
            Task task = new Task(() =>
            {
                Dispatcher.BeginInvoke(new ShowBackDelegate(ShowBack));
                if (mode == 0) SortInternal();
                else SortInternal2();
                Thread.Sleep(500);
                Dispatcher.BeginInvoke(new ShowCardDelegate(ShowCard));
            });
            task.Start();
        }

        public void QuickSortCard(int mode)
        {
            if (mode == 0) SortInternal();
            else SortInternal2();
            ShowCard();
        }

        private void SortInternal()
        {
            CardStack stack = new CardStack(m_cards.ToArray());
            List<Card> sorted = new List<Card>();
            for (int i = (int)CardPoint.CardJoker; i >= (int)CardPoint.CardGJoker; i--) sorted.AddRange(stack[i]);
            for (int c = 8; c >= 0; c--)
            {
                for (int i = (int)CardPoint.Card2; i >= (int)CardPoint.Card3; i--)
                {
                    if (stack[i].Count == c) sorted.AddRange(stack[i]);
                }
            }
            m_cards = sorted;
        }

        private void SortInternal2()
        {
            m_cards.Sort(this);
        }

        public int Compare(Card x, Card y)
        {
            return y.CompareTo(x);
        }

        public ICollection<Card> GetSelectedCards()
        {
            List<Card> cards = new List<Card>();
            for (int i = 0; i < m_selectedList.Count; i++)
            {
                if (m_selectedList[i]) cards.Add(m_cards[i]);
            }
            return cards.ToArray();
        }

        private void SetImageMargin(Image img, bool selected)
        {
#if false
            img.BeginAnimation(Image.MarginProperty, (AnimationTimeline)App.Current.Resources[selected ? "Anim_Card_Select" : "Anim_Card_Unselect"]);
#else
            Thickness thick = img.Margin;
            if (selected) thick.Top -= SelectMargin;
            else thick.Top += SelectMargin;
            img.Margin = thick;
#endif
        }

        public void SetSelectedCards(ICollection<Card> cards)
        {
            for (int i = 0; i < m_cards.Count; i++)
            {
                Card c = m_cards[i];
                if (cards.Contains(c))
                {
                    if (!m_selectedList[i])
                    {
                        m_selectedList[i] = true;
                        Image img = (Image)gridContent.Children[i];
                        SetImageMargin(img, true);
                    }
                }
                else
                {
                    if (m_selectedList[i])
                    {
                        m_selectedList[i] = false;
                        Image img = (Image)gridContent.Children[i];
                        SetImageMargin(img, false);
                    }
                }
            }
        }

        private void ReLayout()
        {
            UIElementCollection coll = gridContent.Children;
            if (coll.Count == 0)
            {
                Width = ShaddowSize;
                Height = ShaddowSize;
                return;
            }

            int margin = Mode == CardGroupMode.Normal ? CardMargin : SmallCardMargin;
            Size size = Mode == CardGroupMode.Normal ? CardSize : SmallCardSize;
            if (Selectable)
            {
                int y = SelectMargin;
                int needWidth = (coll.Count - 1) * margin + (int)size.Width;
                int x = 0;
                Width = needWidth + ShaddowSize;
                Height = SelectMargin + (int)size.Height + ShaddowSize;
                int i = 0;
                foreach (UIElement el in coll)
                {
                    Image img = (Image)el;
                    Thickness thick = img.Margin;
                    thick.Left = x;
                    thick.Top = y;
                    img.Margin = thick;
                    Panel.SetZIndex(img, i);
                    i++;
                    x += margin;
                }
            }
            else
            {
                int y = 0;
                int needWidth = ((coll.Count < WrapCardCount ? coll.Count : WrapCardCount) - 1) * margin + (int)size.Width;
                int x = 0;
                int line = coll.Count % WrapCardCount == 0 ? coll.Count / WrapCardCount : coll.Count / WrapCardCount + 1;
                int lineMargin = Mode == CardGroupMode.Normal ? LineMargin : SmallLineMargin;
                Width = needWidth + ShaddowSize;
                Height = (line - 1) * lineMargin + (int)size.Height + ShaddowSize;
                int i = 0;
                foreach (UIElement el in coll)
                {
                    Image img = (Image)el;
                    Thickness thick = img.Margin;
                    thick.Left = x;
                    thick.Top = y;
                    img.Margin = thick;
                    Panel.SetZIndex(img, i);
                    i++;
                    if (i == WrapCardCount)
                    {
                        x = 0;
                        y += lineMargin;
                    }
                    else x += margin;
                }
            }
        }

        private string GetCardStyleString(int cardStyle)
        {
            switch (CardStyle)
            {
                case 0: return "S1";
                case 1: return "S2";
                case 2: return "S3";
                case 3: return "S4";
                default: throw new Exception("Invalid Card Style!");
            }
        }

        private string GetCardKindString(CardKind cardKind)
        {
            switch (cardKind)
            {
                case CardKind.Spade: return "S";
                case CardKind.Club: return "C";
                case CardKind.Heart: return "H";
                case CardKind.Diamond: return "D";
                default: throw new Exception("Invalid Card Kind!");
            }
        }

        private string GetCardPointString(CardPoint cardPoint)
        {
            switch (cardPoint)
            {
                case CardPoint.CardA: return "A";
                case CardPoint.Card2: return "2";
                case CardPoint.Card3: return "3";
                case CardPoint.Card4: return "4";
                case CardPoint.Card5: return "5";
                case CardPoint.Card6: return "6";
                case CardPoint.Card7: return "7";
                case CardPoint.Card8: return "8";
                case CardPoint.Card9: return "9";
                case CardPoint.Card10: return "10";
                case CardPoint.CardJ: return "J";
                case CardPoint.CardQ: return "Q";
                case CardPoint.CardK: return "K";
                case CardPoint.CardGJoker: return "G_Joker";
                case CardPoint.CardJoker: return "Joker";
                default: throw new Exception("Invalid Card Point!");
            }
        }

        private string GetCardResourceString(int cardStyle, Card card)
        {
            string style = GetCardStyleString(cardStyle);
            string point = GetCardPointString(card.Point);
            if (card.IsJoker) return string.Format("{0}_{1}", style, point);
            else
            {
                string kind = GetCardKindString(card.Kind.Value);
                return string.Format("{0}_{1}_{2}", style, kind, point);
            }
        }
    }

    public enum CardGroupMode
    {
        Normal,
        Small
    }
}
